package com.taomus.mytools.simplec.generate;

import java.util.HashMap;
import java.util.Map;
import java.util.Queue;

import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.taomus.mytools.simplec.common.SyntaxTuple;
import com.taomus.mytools.simplec.common.Token;
import com.taomus.mytools.simplec.common.WordEnum;
import com.taomus.mytools.simplec.utils.AsmUtils;
import com.taomus.mytools.simplec.utils.ErrorEnum;
import com.taomus.mytools.simplec.utils.ErrorUtils;
import com.taomus.mytools.simplec.utils.GeneratorAdapterUtils;
import com.taomus.mytools.simplec.utils.ICode;
import static com.taomus.mytools.simplec.common.SyntaxTuple.*;

public class GenFunction implements ICode {
	AsmUtils as;
	Queue<SyntaxTuple> tupleQ;
	Map<String, Integer> localNum = new HashMap<>();
	Map<String, Label> labelMap = new HashMap<>();

	public GenFunction(final AsmUtils as, final Queue<SyntaxTuple> q) {
		this.as = as;
		this.tupleQ = q;
	}

	@Override
	public void code(GeneratorAdapterUtils adapter) {
		int fAgrsNum = 0;
		while (true) {
			SyntaxTuple st = tupleQ.poll();
			ErrorUtils.error(st);
			WordEnum opcode = st.getOpCode();
			if (opcode == WordEnum.FUNCR) {
				break;
			}
			switch (opcode) {
			case FAGRSNUM:
				fAgrsNum = funcAgrsNumber(adapter,st);
				break;
			case FRAGRS:
				this.funcArgs(adapter,st, fAgrsNum);
				break;
			case FCALL:
				StringBuilder method = new StringBuilder("Object ");
				method.append(st.getSymbol(T1));
				method.append("(Object[])");
				adapter.invokeVirtual(method.toString());
				break;	
			case MOV:
				this.mov(st,adapter);
			default:
				//四则运算
				//关系运算
				break;
			}
		}
	}

	/**
	 * 函数参数(实际参数)
	 * 
	 * @param fadapter
	 * @param fAgrsNum
	 */
	private void funcArgs(GeneratorAdapterUtils adapter,SyntaxTuple st,int fAgrsNum) {
		Token t1 = st.t1();
		for (int i = 0; i < fAgrsNum; i++) {
			adapter.push(i);
			if (t1.getCode() == WordEnum.NUMBER) {
				adapter.push(Integer.parseInt(t1.getSymbol()));
				adapter.invokeStatic(Integer.class, "Integer valueOf(int)");
				adapter.arrayStore(Type.getType(Object.class));
				if (i + 1 != fAgrsNum) {
					adapter.dup();
				}
			} else if (t1.getCode() == WordEnum.ID) {
				switch (t1.getLevel()) {
				case 0:
					getGlobalVariable(adapter, t1,true);
					adapter.arrayStore(Type.getType(Object.class));
					break;
				default:
					getLocalVariable(adapter, t1,true);
					adapter.arrayStore(Type.getType(Object.class));
					break;
				}
				if (i + 1 != fAgrsNum) {
					adapter.dup();
				}
			}
			SyntaxTuple temp = tupleQ.peek();
			if (temp.getOpCode() == WordEnum.FRAGRS) {
				st = tupleQ.poll();
			}
		}
	}


	private void getLocalVariable(GeneratorAdapterUtils adapter, Token tk) {
		this.getLocalVariable(adapter, tk, false);
	}
	private void getLocalVariable(GeneratorAdapterUtils adapter, Token tk,boolean isArgs) {
		String fieldName = tk.getSymbol();
		switch (tk.getCode()) {
		case _INT:
			adapter.loadLocal(localNum.get(fieldName), int.class);
			if(isArgs){
				adapter.invokeStatic(Integer.class, "Integer valueOf(int)");
			}
			break;
		case _DOUBLE:
			adapter.loadLocal(localNum.get(fieldName), double.class);
			if(isArgs){
				adapter.invokeStatic(Double.class, "Double valueOf(double)");
			}
			break;
		default:
			ErrorUtils.error(ErrorEnum.TYPE_ERROR, tk);
			break;
		}
	}

	private void getGlobalVariable(GeneratorAdapterUtils adapter, Token tk) {
		this.getGlobalVariable(adapter, tk, false);
	}
	private void getGlobalVariable(GeneratorAdapterUtils adapter, Token tk,boolean isArgs) {
		String fieldName = tk.getSymbol();
		adapter.loadThis();
		switch (tk.getCode()) {
		case _INT:
			adapter.getField(fieldName, int.class);
			if(isArgs){
				adapter.invokeStatic(Integer.class, "Integer valueOf(int)");
			}
			break;
		case _DOUBLE:
			adapter.getField(fieldName, double.class);
			if(isArgs){
				adapter.invokeStatic(Double.class, "Double valueOf(double)");
			}
			break;
		default:
			ErrorUtils.error(ErrorEnum.TYPE_ERROR, tk);
			break;
		}
	}

	private void setLocalVariable(GeneratorAdapterUtils adapter, Token tk){
		String fieldName = tk.getSymbol();
		int local = 0;
		switch (tk.getCode()) {
		case _INT:
			local = adapter.newLocal(int.class);
			adapter.storeLocal(local,int.class);
			localNum.put(fieldName, local);
			break;
		case _DOUBLE:
			local = adapter.newLocal(double.class);
			adapter.storeLocal(local,double.class);
			localNum.put(fieldName, local);
			break;
		default:
			ErrorUtils.error(ErrorEnum.TYPE_ERROR, tk);
			break;
		}
	}
	private void setGlobalVariable(GeneratorAdapterUtils adapter, Token tk){
		String fieldName = tk.getSymbol();
		adapter.loadThis();
		switch (tk.getCode()) {
		case _INT:
			adapter.putField(fieldName, int.class);
			break;
		case _DOUBLE:
			adapter.putField(fieldName, double.class);
			break;
		default:
			ErrorUtils.error(ErrorEnum.TYPE_ERROR, tk);
			break;
		}
	}
	/**
	 * 函数参数数量
	 */
	private int funcAgrsNumber(GeneratorAdapterUtils adapter,SyntaxTuple st) {
		int fAgrsNum = Integer.parseInt(st.t1().getSymbol());
		adapter.loadThis();
		adapter.push(fAgrsNum);
		adapter.newArray(Type.getType(Object.class));
		adapter.dup();
		return fAgrsNum;
	}

	private void mov(SyntaxTuple st,GeneratorAdapterUtils adapter){
		Token t1 = st.getToken(T1);
		Token t3 = st.getToken(T3);
		if(t3.getCode() == WordEnum.ID){
			if(t3.getLevel() == 0){
				this.getGlobalVariable(adapter, t3);
			}else{
				this.getLocalVariable(adapter, t3);
			}
		}else if(t3.getCode() == WordEnum.NUMBER){
			adapter.push(Integer.parseInt(t3.getSymbol()));
		}else if(t3.getCode() == WordEnum.TEMP){
			this.getLocalVariable(adapter, t3);
		}else if(t3.getCode() == WordEnum.FAGRS) {
			//TODO 还需要更详细的数据才能推到出数据类型
			adapter.loadArg(0);
			int arg = Integer.parseInt(st.getSymbol(T3).split("@")[1]);
			adapter.push(arg - 1);
			adapter.visitInsn(Opcodes.AALOAD);
			adapter.checkCast(Type.getType(Integer.class));
			adapter.invokeVirtual(Integer.class,"int intValue()");
		}
		if(t1.getLevel() == 0){
			this.setGlobalVariable(adapter,t1);
		}else{
			this.setLocalVariable(adapter,t1);
		}
	}
}
